package com.lyc.common.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.beanutils.PropertyUtilsBean;
import org.springframework.cglib.beans.BeanGenerator;

import com.google.common.collect.Maps;
import cn.hutool.core.collection.CollUtil;
import lombok.SneakyThrows;
import net.sf.cglib.beans.BeanMap;

/**
 * @Author 李大神仙
 * @Date 2024/3/11 21:50
 * @Version 1.0
 */
public class ReflectUtil {
    public static <T> T getTarget(Object dest, Map<String,Object> addProperties,Class<T> tClass){
        PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
        PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
        Map<String, Class> propertyMap = Maps.newHashMap();
        for (PropertyDescriptor d : descriptors){
            if (!"class".equalsIgnoreCase(d.getName())){
                propertyMap.put(d.getName(),d.getPropertyType());
            }
        }
        //add extra properties
        addProperties.forEach((k,v) -> propertyMap.put(k,v.getClass()));
        //new dynamic bean
        DynamicBean dynamicBean = new DynamicBean(dest.getClass(),propertyMap);
        //add old value
        propertyMap.forEach((k,v) -> {
            try {
                //filter extra properties
                if (!addProperties.containsKey(k)){
                    dynamicBean.setValue(k,propertyUtilsBean.getNestedProperty(dest,k));
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        //add extra value
        addProperties.forEach((k,v) -> {
            try {
                dynamicBean.setValue(k,v);
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        return (T) dynamicBean.getTarget();
    }

    public static <T> T getTargetDynamic(Map<String,Object> addProperties,Class<T> tClass){
        Map<String,Class> propertyMap = Maps.newHashMap();
        //add extra properties
        addProperties.forEach((k,v) -> propertyMap.put(k,v.getClass()));
        //new dynamic bean
        DynamicBean dynamicBean = new DynamicBean(tClass,propertyMap);
        //add old value
        //add extra value
        addProperties.forEach((k,v) -> {
            try {
                dynamicBean.setValue(k,v);
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        return (T) dynamicBean.getTarget();
    }

    public static Object getObjectValueByField(String fieldName,Object object,Class<?> clazz){
        try {
            //获取字段
            //从当前类中查找
            Field declaredField = null;
            try {
                declaredField = clazz.getDeclaredField(fieldName);
            }catch (NoSuchFieldException e){
            }
            if (null != declaredField){
                declaredField.setAccessible(Boolean.TRUE);
                Object o = declaredField.get(object);
                declaredField.setAccessible(Boolean.FALSE);
                return o;
            }
            //从父类中查找 仅限public方法
            Field field = null;
            try {
                Class<?> superclass = clazz.getSuperclass();
                field = superclass.getDeclaredField(fieldName);
            }catch (NoSuchFieldException e){
            }
            if (null != field){
                field.setAccessible(Boolean.TRUE);
                Object o = field.get(object);
                field.setAccessible(Boolean.FALSE);
                return o;
            }
            return null;
        }catch (Exception e){
            return null;
        }
    }

    public static class DynamicBean{
        /**
         * 目标对象
         */
        private Object target;

        /**
         * 属性集合
         */
        private BeanMap beanMap;

        public DynamicBean(Class superclass,Map<String,Class> propertyMap) {
            this.target = generateBean(superclass,propertyMap);
            this.beanMap = BeanMap.create(this.target);
        }

        /**
         * bean 添加属性和值
         * @param property
         * @param value
         */
        public void setValue(String property,Object value){
            beanMap.put(property,value);
        }

        /**
         * 获取属性值
         * @param property
         * @return
         */
        public Object getValue(String property){
            return beanMap.get(property);
        }

        /**
         * 获取对象
         * @return
         */
        public Object getTarget(){
            return this.target;
        }

        private Object generateBean(Class superclass,Map<String,Class> propertyMap){
            BeanGenerator generator = new BeanGenerator();
            if (null != superclass){
                generator.setSuperclass(superclass);
            }
            BeanGenerator.addProperties(generator,propertyMap);
            return generator.create();
        }
    }

    public static Object getListValueByField(String fieldName, List list,Class<?> clazz){
        try {
            //获取字段
            if (CollUtil.isEmpty(list)){
                return null;
            }
            StringBuilder stringBuilder = new StringBuilder();
            //获取字段数组
            for (int i = 0; i < list.size(); i++) {
                Object o = list.get(i);
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(Boolean.TRUE);
                Object ob = field.get(o);
                stringBuilder = ob == null ? stringBuilder.append("\n") : stringBuilder.append(ob).append("\n");
                field.setAccessible(Boolean.FALSE);
            }
            return stringBuilder.deleteCharAt(stringBuilder.lastIndexOf("\n")).toString();
        }catch (Exception e){
            return null;
        }
    }

    public static boolean hasField(Class clzz,String fieldName){
        //从当前类中查找
        Field declaredField = null;
        try {
            declaredField = clzz.getDeclaredField(fieldName);
        }catch (NoSuchFieldException e){
        }
        if (null != declaredField){
            return true;
        }
        //递归从父类查找直到object
        Field field = getField(clzz,fieldName);
        if (null != field){
            return true;
        }
        return false;
    }

    private static Field getField(Class clzz,String fieldName){
        Field field;
        Class<?> superclass = null;
        try {
            superclass = clzz.getSuperclass();
            if (superclass == null){
                //一直找到object类
                return null;
            }
            field = superclass.getDeclaredField(fieldName);
            if (field != null){
                return field;
            }
        }catch (NoSuchFieldException e){
        }
        return getField(superclass,fieldName);
    }

    private static Object getObjectValueByField2(String fieldName,Object object){
        //获取当前的属性
        Class<?> clazz =  object.getClass();
        //首先判断当前实体类及父类是否存在这个字段
        boolean hasFieldFlag = hasField(clazz,fieldName);
        if (hasFieldFlag){
            Object value = getObjectValueByField(fieldName,object,clazz);
            if (value instanceof List){
                List field = (List)value;
                value = CollUtil.isEmpty(field) ? null :
                        ReflectUtil.getListValueByField(fieldName,field,field.get(0).getClass());
            }
            return value;
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        //获取不是基本类型的属性
        List<Field> fieldList = Arrays.stream(declaredFields).filter(
                e -> !e.getType().isPrimitive()).collect(Collectors.toList());
        //递归判断是否这些类属性是否存在以及类属性的类属性是否存在
        return aaa(fieldName,fieldList,object);
    }

    @SneakyThrows
    public static Object aaa(String fieldName,List<Field> fieldList,Object object){
        for (Field field : fieldList){
            field.setAccessible(Boolean.TRUE);
            Object o = field.get(object);
            field.setAccessible(Boolean.FALSE);
            if (Objects.isNull(o)){
                continue;
            }
            if (field.getType().isAssignableFrom(List.class)){
                //判断是否list
                Type genericType = field.getGenericType();
                if (genericType instanceof ParameterizedType){
                    ParameterizedType pt = (ParameterizedType)genericType;
                    Class genericClazz = (Class) pt.getActualTypeArguments()[0];
                    //同样判断是否存在相应的字段
                    List list = (List) o;
                    if (hasField(genericClazz,fieldName)){
                        return getListValueByField(fieldName,list,genericClazz);
                    }else {
                        //继续获取非基础类型的属性进行判断
                        Field[] newFields = genericClazz.getDeclaredFields();
                        List<Field> newFieldList = Arrays.stream(newFields).filter(e -> !e.getType().isPrimitive())
                                .collect(Collectors.toList());
                        if (CollUtil.isEmpty(newFieldList)){
                            return null;
                        }else {
                            aaa(fieldName,newFieldList,o);
                        }
                    }
                }
            }else {
                //如果不是list，那么则按照上述操作继续
                return getObjectValueByField2(fieldName,o);
            }
        }
        return null;
    }
}
